package querysearcher;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

import common.Activity;

public class SearchMain {

	private SearchMain() {
	}

	public static void main(String[] args) throws ParseException, CorruptIndexException, IOException {
		String indexDir = "index";
		boolean raw = false;
		boolean paging = true;
		int hitsPerPage = 10;

		String queries = null;
		IndexReader reader = IndexReader.open(FSDirectory.open(new File(
				indexDir)), true);
		Searcher searcher = new IndexSearcher(reader);
		Analyzer analyzer = new PaodingAnalyzer();

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in,	"UTF-8"));
		String[] fields = { Activity.NAME,
							Activity.BEGINTIME,
							Activity.FINISHTIME,
							Activity.AREA,
							Activity.ORGANIZERS,
							Activity.KIND,
							Activity.SUMMARY };
		BooleanClause.Occur[] occurs = { BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD,
										BooleanClause.Occur.SHOULD };
		while (true) {
			System.out.println("Enter query: ");
			String line = in.readLine();
			if (line == null || line.length() == -1)
				break;

			line = line.trim();
			if (line.length() == 0)
				break;

			Query query = MultiFieldQueryParser.parse(Version.LUCENE_29, line, fields, occurs, analyzer);
			System.out.println("Searching for: " + query.toString());

			if (paging) {
				doPagingSearch(in, searcher, query, hitsPerPage, raw,
						queries == null);
			} else {
				doStreamingSearch(searcher, query);
			}
		}
		reader.close();
	}

	/**
	 * This method uses a custom HitCollector implementation which simply prints
	 * out the docId and score of every matching document.
	 * 
	 * This simulates the streaming search use case, where all hits are supposed
	 * to be processed, regardless of their relevance.
	 */
	public static void doStreamingSearch(final Searcher searcher, Query query)
			throws IOException {
		Collector streamingHitCollector = new Collector() {
			private Scorer scorer;
			private int docBase;

			// simply print docId and score of every matching document
			public void collect(int doc) throws IOException {
				System.out.println("doc=" + doc + docBase + " score="
						+ scorer.score());
			}

			public boolean acceptsDocsOutOfOrder() {
				return true;
			}

			public void setNextReader(IndexReader reader, int docBase)
					throws IOException {
				this.docBase = docBase;
			}

			public void setScorer(Scorer scorer) throws IOException {
				this.scorer = scorer;
			}

		};

		searcher.search(query, streamingHitCollector);
	}

	/**
	 * This demonstrates a typical paging search scenario, where the search
	 * engine presents pages of size n to the user. The user can then go to the
	 * next page if interested in the next hits.
	 * 
	 * When the query is executed for the first time, then only enough results
	 * are collected to fill 5 result pages. If the user wants to page beyond
	 * this limit, then the query is executed another time and all hits are
	 * collected.
	 * 
	 */
	public static void doPagingSearch(BufferedReader in, Searcher searcher,
			Query query, int hitsPerPage, boolean raw, boolean interactive)
			throws IOException {

		// Collect enough docs to show 5 pages
		TopScoreDocCollector collector = TopScoreDocCollector.create(
				5 * hitsPerPage, false);
		searcher.search(query, collector);
		ScoreDoc[] hits = collector.topDocs().scoreDocs;

		int numTotalHits = collector.getTotalHits();
		System.out.println(numTotalHits + " total matching documents");

		int start = 0;
		int end = Math.min(numTotalHits, hitsPerPage);

		while (true) {
			if (end > hits.length) {
				System.out.println("Only results 1 - " + hits.length + " of "
								+ numTotalHits + " total matching documents collected.");
				System.out.println("Collect more (y/n) ?");
				String line = in.readLine();
				if (line.length() == 0 || line.charAt(0) == 'n') {
					break;
				}

				collector = TopScoreDocCollector.create(numTotalHits, false);
				searcher.search(query, collector);
				hits = collector.topDocs().scoreDocs;
			}

			end = Math.min(hits.length, start + hitsPerPage);

			for (int i = start; i < end; i++) {
				if (raw) { 				// output raw format
					System.out.println("doc=" + hits[i].doc + " score="
							+ hits[i].score);
					continue;
				}

				Document doc = searcher.doc(hits[i].doc);
				String url = doc.get(Activity.ACTURL);
				System.out.println((i + 1) + ". " + url);
				System.out.println( "\t" + doc.get(Activity.NAME) + "\t"
									+ doc.get(Activity.BEGINTIME) + " - "
									+ doc.get(Activity.FINISHTIME) + "\t"
									+ doc.get(Activity.AREA) );
			}

			if (!interactive) {
				break;
			}

			if (numTotalHits >= end) {
				boolean quit = false;
				while (true) {
					System.out.print("Press ");
					if (start - hitsPerPage >= 0) {
						System.out.print("(p)revious page, ");
					}
					if (start + hitsPerPage < numTotalHits) {
						System.out.print("(n)ext page, ");
					}
					System.out.println("(q)uit or enter number to jump to a page.");

					String line = in.readLine();
					if (line==null || line.length()==0 || line.charAt(0)=='q') {
						quit = true;
						break;
					}
					if (line.charAt(0) == 'p') {
						start = Math.max(0, start - hitsPerPage);
						break;
					} else if (line.charAt(0) == 'n') {
						if (start + hitsPerPage < numTotalHits) {
							start += hitsPerPage;
						}
						break;
					} else {
						int page = Integer.parseInt(line);
						if ((page - 1) * hitsPerPage < numTotalHits) {
							start = (page - 1) * hitsPerPage;
							break;
						} else {
							System.out.println("No such page");
						}
					}
				}
				if (quit)	break;
				end = Math.min(numTotalHits, start + hitsPerPage);
			}

		}

	}
}
